﻿using System.Collections.Generic;
using Moria.Silverlight.Enum;

namespace Moria.Silverlight.Model
{
    /// <summary>
    /// store_type
    /// </summary>
    public class Store
    {
        public const int MaxStores = 6;
        public const int STORE_INVEN_MAX = 24;   /* Max number of discrete objs in inven  */
        public const int STORE_CHOICES = 26;   /* NUMBER of items to choose stock from  */
        public const int STORE_MIN_INVEN = 10;   /* Min diff objs in stock for auto sell  */
        public const int STORE_MAX_INVEN = 18;   /* Max diff objs in stock for auto buy   */
        public const int STORE_TURN_AROUND = 9;   /* Amount of buying and selling normally */
        public const int OBJ_TOWN_LEVEL = 7;    /* Town object generation level	       */

        /* object_list[] index of objects that may appear in the store */
        private readonly int[,] store_choice = 
        {
        /* General Store */     {366,365,364,84,84,365,123,366,365,350,349,348,347,346,346,345,345,345,344,344,344,344,344,344,344,344},
        /* Armory	 */         {94,95,96,109,103,104,105,106,110,111,112,114,116,124,125,126,127,129,103,104,124,125,91,92,95,96},
        /* Weaponsmith	 */     {29,30,34,37,45,49,57,58,59,65,67,68,73,74,75,77,79,80,81,83,29,30,80,83,80,83},
        /* Temple	 */         {322,323,324,325,180,180,233,237,240,241,361,362,57,58,59,260,358,359,265,237,237,240,240,241,323,359},
        /* Alchemy shop	 */     {173,174,175,351,351,352,353,354,355,356,357,206,227,230,236,252,253,352,353,354,355,356,359,363,359,359},
        /* Magic-User store*/   {318,141,142,153,164,167,168,140,319,320,320,321,269,270,282,286,287,292,293,294,295,308,269,290,319,282}
        };

        public Store(StoreType storeType)
        {
            this.store_inven = new List<InventoryRecord>();
            this.Type = storeType;

            int i = StoreOwner.owners.Count / MaxStores;
            this.Owner = StoreOwner.owners[MaxStores * (Dice.RandInt(i) - 1) + (int)storeType];
            this.InsultCur = 0;
            this.Open = 0;
            this.GoodBuy = 0;
            this.BadBuy = 0;

        }

        public StoreType Type { get; private set; }
        public int StoreNum { get { return (int)this.Type; } }
        public int Open { get; private set; }
        public int InsultCur { get; set; }
        public StoreOwner Owner { get; private set; }
        public int Counter { get { return this.store_inven.Count; } }
        public int GoodBuy { get; private set; }
        public int BadBuy { get; private set; }
        public List<InventoryRecord> store_inven { get; private set; }

        /// <summary>
        /// Destroy an item in the stores inventory.  Note that if
        /// "one_of" is false, an entire slot is destroyed
        /// </summary>
        internal void store_destroy(int item_val, bool one_of)
        {
            InventoryItem i_ptr = this.store_inven[item_val].InventoryItem;

            // for single stackable objects, only destroy one half on average,
            //this will help ensure that general store and alchemist have
            //reasonable selection of objects 
            int number = 1;
            if ((i_ptr.SubCategory >= InventoryItem.ITEM_SINGLE_STACK_MIN) &&
                (i_ptr.SubCategory <= InventoryItem.ITEM_SINGLE_STACK_MAX))
            {
                if (!one_of) number = Dice.RandInt((int)i_ptr.ItemCount);
            }
            else
            {
                number = i_ptr.ItemCount;
            }

            if (number != i_ptr.ItemCount)
            {
                i_ptr.ItemCount -= number;
            }
            else
            {
                this.store_inven.RemoveAt(item_val);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        internal void store_create()
        {
            int tries = 0;
            do
            {
                int i = store_choice[this.StoreNum, Dice.RandInt(STORE_CHOICES) - 1];
                InventoryItem t_ptr = new InventoryItem(i);
                t_ptr.magic_treasure(OBJ_TOWN_LEVEL);
                if (this.store_check_num(t_ptr))
                {
                    if ((t_ptr.Cost > 0) &&	/* Item must be good	*/
                        (t_ptr.Cost < this.Owner.max_cost))
                    {
                        /* equivalent to calling ident_spell(), except will not
                       change the object_ident array */
                        t_ptr.store_bought();
                        this.store_carry( t_ptr);
                        tries = 10;
                    }
                }
                tries++;
            }
            while (tries <= 3);
        }

        private int store_carry(InventoryItem t_ptr)
        {
            int ipos = -1;
            int dummy = 0;
            int icost = 0;
            if (this.sell_price(out icost, out dummy, t_ptr) > 0)
            {
                int item_val = 0;
                int item_num = t_ptr.ItemCount;
                bool flag = false;
                ItemType typ = t_ptr.Type;
                int subt = t_ptr.SubCategory;
                do
                {
                    InventoryItem i_ptr = this.store_inven[item_val];
                    if (typ == i_ptr.Type)
                    {
                        if (subt == i_ptr.SubCategory && /* Adds to other item	*/
                        subt >= InventoryItem.ITEM_SINGLE_STACK_MIN
                        && (subt < InventoryItem.ITEM_GROUP_MIN || i_ptr.Misc == t_ptr.Misc))
                        {
                            ipos = item_val;
                            i_ptr.ItemCount += item_num;
                            /* must set new scost for group items, do this only for items
                               strictly greater than group_min, not for torches, this
                               must be recalculated for entire group */
                            if (subt > InventoryItem.ITEM_GROUP_MIN)
                            {
                                sell_price(out icost,out dummy, i_ptr);
                                this.store_inven[item_val].Cost = -icost;
                            }
                            /* must let group objects (except torches) stack over 24
                               since there may be more than 24 in the group */
                            else if (i_ptr.ItemCount > 24)
                                i_ptr.ItemCount = 24;
                            flag = true;
                        }
                    }
                    else if (typ > i_ptr.Type)
                    {		/* Insert into list		*/
                        this.insert_store(item_val, icost, t_ptr);
                        flag = true;
                        ipos = item_val;
                    }
                    item_val++;
                }
                while ((item_val < this.Counter) && (!flag));
                if (!flag)	/* Becomes last item in list	*/
                {
                    this.insert_store(this.Counter, icost, t_ptr);
                    ipos = this.Counter - 1;
                }
            }
            return ipos;
        }
        /// <summary>
        ///  Asking price for an item				-
        /// </summary>
        int sell_price(out int max_sell, out int min_sell, InventoryItem item)
        {
            int i = item.Value;
            min_sell =
                max_sell = -1;

            /* check item.cost in case it is cursed, check i in case it is damaged */
            if ((item.Cost > 0) && (i > 0))
            {
                // i = i * rgold_adj[this.Owner.Race][py.misc.prace] / 100;
                if (i < 1) i = 1;
                max_sell = i * this.Owner.max_inflate / 100;
                min_sell = i * this.Owner.min_inflate / 100;
                if (min_sell > max_sell) min_sell = max_sell;
                return (i);
            }
            /* don't let the item get into the store inventory */
            return (0);
        }

        /* Insert INVEN_MAX at given location	*/
        private void insert_store(int pos, int icost, InventoryItem i_ptr)
        {
            this.store_inven.Insert(pos, new InventoryRecord() { Cost = icost, InventoryItem = i_ptr });
        }

        private bool store_check_num(InventoryItem t_ptr)
        {
            if (this.Counter < STORE_INVEN_MAX)
            {
                return true;
            }
            else if (t_ptr.SubCategory >= InventoryItem.ITEM_SINGLE_STACK_MIN)
            {
                foreach (InventoryRecord i_ptr in this.store_inven)
                {
                    /* note: items with subval of gte ITEM_SINGLE_STACK_MAX only stack if their subvals match */
                    if (i_ptr.InventoryItem.Type == t_ptr.Type && i_ptr.InventoryItem.SubCategory == t_ptr.SubCategory
                        && (i_ptr.InventoryItem.ItemCount + t_ptr.ItemCount < 256)
                        && (t_ptr.SubCategory < InventoryItem.ITEM_GROUP_MIN
                        || (i_ptr.InventoryItem.Misc == t_ptr.Misc)))
                    {
                        return true;
                    }
                }
            }
            return false;
        }
    }
}
